/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package regulatoryNetworks;

import functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.parsers.GoaParser;
import functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.parsers.OboParser;
import functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.units.AnnotatedGene;
import functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.units.GoTerm;
import functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.units.GoTermPair;
import functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.units.Ontology;
import functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.units.OntologyType;
import functionalEnrichment.semanticSimilarity.semanticSimilarityCalculator.utils.FileUtils;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import jsc.independentsamples.MannWhitneyTest;
import jsc.tests.H1;
import org.apache.commons.math.stat.descriptive.SummaryStatistics;
import regulatoryNetworks.MinetResultsParser;
import units.CellType;
import units.MonkeyType;
import utils.Handle;

/**
 *
 * @author jamie
 */
public class GoTermPairTest {

    private File oboFile = new File("/Users/jamie/Dropbox/monkeys/GO/annotations/gene_ontology.obo");
    private File goaFile = new File("/Users/jamie/Dropbox/monkeys/GO/annotations/gene_association.goa_human");
    private File outputFile = new File("/Users/jamie/Dropbox/monkeys/GO/miTest/");
    
    private Map<String, Double> minetResults_AL;
    private Map<String, Double> minetResults_AP;
    private Map<String, Double> minetResults_ML;
    private Map<String, Double> minetResults_MP;
    private List<String> probeIds_AL;
    private List<String> probeIds_AP;
    private List<String> probeIds_ML;
    private List<String> probeIds_MP;
    private Handle handle = Handle.getConnectedHandle("monkeys");
//    private Map<String, List<Integer>> probeToGeneId;
    private Map<String, List<String>> probeToSwissId;
    private Map<String, List<String>> swissToProbe;
    private TreeMap<GoTerm, List<String>> goTermMap;
//    private TreeSet<Integer> nonredundantGeneIds;
    private TreeSet<String> nonredundantSwissIds;
    private boolean cellTypeAnalysis = false;
    private OntologyType ontologyType;

    double minMI = 0;
    double minFC = 0;
    double minDif = 0.1;
    int minN = 2;

    private boolean limitToImmuneAndCellCycle = true;


    public void runTest(OntologyType ontologyType) {
        this.ontologyType = ontologyType;
        OboParser oboParser = new OboParser();
        Ontology ontology_mf = oboParser.getOntology(oboFile, ontologyType);
        GoaParser goaParser = new GoaParser(goaFile, ontology_mf);
        Map<String, AnnotatedGene> annotatedGenes = goaParser.getAnnotatedGenes();
        MinetResultsParser minetResultsParser;
        try {
            minetResultsParser = new MinetResultsParser();
            minetResultsParser.setMiThreshold(0);
            minetResults_AL = minetResultsParser.getResultsFromFile(MonkeyType.A, CellType.L);
            probeIds_AL = minetResultsParser.getProbeIds();

            minetResultsParser = new MinetResultsParser();
            minetResultsParser.setMiThreshold(0);
            minetResults_AP = minetResultsParser.getResultsFromFile(MonkeyType.A, CellType.P);
            probeIds_AP = minetResultsParser.getProbeIds();

            minetResultsParser = new MinetResultsParser();
            minetResultsParser.setMiThreshold(0);
            minetResults_ML = minetResultsParser.getResultsFromFile(MonkeyType.M, CellType.L);
            probeIds_ML = minetResultsParser.getProbeIds();

            minetResultsParser = new MinetResultsParser();
            minetResultsParser.setMiThreshold(0);
            minetResults_MP = minetResultsParser.getResultsFromFile(MonkeyType.M, CellType.P);
            probeIds_MP = minetResultsParser.getProbeIds();
        } catch (IOException ex) {
            Logger.getLogger(GoTermPairTest.class.getName()).log(Level.SEVERE, null, ex);
        }

//        probeToGeneId = getProbeIdToGeneId();
        nonredundantSwissIds = getNonRedundantSwissIds();
        swissToProbe = getSwissIdToProbeId();
        goTermMap = getGoTermMap(annotatedGenes);
        List<GoTermPair> termPairs = getGoTermPairs();
        try {
            outputResults(termPairs, new File(outputFile, ontologyType.name() + ".txt"));
        } catch (IOException ex) {
            Logger.getLogger(GoTermPairTest.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private TreeMap<GoTerm, List<String>> getGoTermMap(Map<String, AnnotatedGene> annotatedGenes) {
        TreeMap<GoTerm, List<String>> termMap = new TreeMap<GoTerm, List<String>>();
        for (String swissId : nonredundantSwissIds) {
            if (annotatedGenes.containsKey(swissId)) {
                AnnotatedGene ag = annotatedGenes.get(swissId);
                List<GoTerm> terms = ag.getAllTermsIncludingParents();
                for (GoTerm term : terms) {
                    if (!termMap.containsKey(term)) {
                        termMap.put(term, new ArrayList<String>());
                    }
                    termMap.get(term).add(swissId);
                }
            }
        }
        return termMap;
    }

    private TreeSet<String> getNonRedundantSwissIds() {
        TreeSet<String> swissIdsSet = new TreeSet<String>();
        List[] probeIdSets = {probeIds_AL, probeIds_AP, probeIds_ML, probeIds_MP};
        for (List probeIds : probeIdSets) {
            List<String> probeIdsParam = (List<String>) probeIds;
            for (String probeId : probeIdsParam) {
                List<String> swissIds = getSwissIds(probeId);
                for (String swissId : swissIds) {
                    if (!swissIdsSet.contains(swissId)) {
                        swissIdsSet.add(swissId);
                    }
                }
            }
        }
        return swissIdsSet;
    }

    private Map<String, List<String>> getSwissIdToProbeId() {
        HashMap<String, List<String>> lookup = new HashMap<String, List<String>>();
        for (String swissId : nonredundantSwissIds) {
            if (!lookup.containsKey(swissId)) {
                lookup.put(swissId, new ArrayList<String>());
            }
            lookup.get(swissId).addAll(getProbeIds(swissId));
        }
        return lookup;
    }



    private List<String> getSwissIds(String probeIdString) {
        String sql = "select distinct swissProtAccession from probeIdLookup t1, swissProtLookup t2 where t1.gId=t2.gId and t1.probeId=" + probeIdString;
        List<String> swissIds = new ArrayList<String>();
        ResultSet rs = handle.runSQLquery(sql);
        try {
            while (rs.next()) {
                String probeId = rs.getString("swissProtAccession");
                swissIds.add(probeId);
            }
            rs.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        return swissIds;
    }

    private List<String> getProbeIds(String swissIdString) {
        String sql = "select distinct probeId from probeIdLookup t1, swissProtLookup t2 where t1.gId=t2.gId and t2.swissProtAccession='" + swissIdString+"'";
        List<String> probeIds = new ArrayList<String>();
        ResultSet rs = handle.runSQLquery(sql);
        try {
            while (rs.next()) {
                String probeId = rs.getString("probeId");
                probeIds.add(probeId);
            }
            rs.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        return probeIds;
    }

    private List<GoTermPair> getGoTermPairs() {
        List<GoTermPair> pairs = new ArrayList<GoTermPair>();
        List<GoTerm> terms = new ArrayList<GoTerm>();
        terms.addAll(goTermMap.keySet());
        for (int i = 0; i < terms.size(); i++) {
            GoTerm gt1 = terms.get(i);
            for (int j = i; j < terms.size(); j++) {
                GoTerm gt2 = terms.get(j);
                if(ontologyType.getAccession()!=gt1.getId() && ontologyType.getAccession()!=gt2.getId() && include(gt1) && include(gt2)){
                    GoTermPair gtp = new GoTermPair(gt1, gt2);
                    pairs.add(gtp);
                }
            }
        }
        return pairs;
    }

    private void outputResults(List<GoTermPair> termPairs, File outFile) throws IOException {
        BufferedWriter writer = FileUtils.getBufferedWriter(outFile);
        for (GoTermPair gtp : termPairs) {
            Result r = getResult(gtp);
            if(r!=null){
                writer.append(r.toString()+"\n");
                writer.flush();
            }
        }
        writer.close();
    }

    private Result getResult(GoTermPair gtp) {
        List<Double> mi_al = getMIValues(minetResults_AL, gtp);
        List<Double> mi_ap = getMIValues(minetResults_AP, gtp);
        List<Double> mi_ml = getMIValues(minetResults_ML, gtp);
        List<Double> mi_mp = getMIValues(minetResults_MP, gtp);

        List<Double> listA = new ArrayList<Double>();
        List<Double> listB = new ArrayList<Double>();
        String description = gtp.getUniqueId() + "_";
        if (cellTypeAnalysis) {
            listA.addAll(mi_al);
            listA.addAll(mi_ml);
            listB.addAll(mi_ap);
            listB.addAll(mi_mp);
            description = description + "LvsP";
        } else {
            listA.addAll(mi_al);
            listA.addAll(mi_ap);
            listB.addAll(mi_ml);
            listB.addAll(mi_mp);
            description = description + "AvsM";
        }
        description = description+"\t"+gtp.getGoTerm1().getName()+"\t"+gtp.getGoTerm2().getName();

        SummaryStatistics ss1 = getSummaryStatistics(listA);
        SummaryStatistics ss2 = getSummaryStatistics(listB);

        Result result = new Result(description);
        result.setSs1(ss1);
        result.setSs2(ss2);
        result.calculateFoldChange();
        if(ss1.getN()>=minN && ss2.getN()>minN && (ss1.getMean()>=minMI || ss2.getMean()>=minMI) && result.getFoldChange()>=minFC && result.getDifference()>=minDif){
            MannWhitneyTest mwut = new MannWhitneyTest(asDoubles(listA), asDoubles(listB), H1.NOT_EQUAL);
            result.setTestStatistic(mwut.getTestStatistic());
            result.setpValue(mwut.getSP());
            if(result.getpValue()<1.0){
                System.out.println(result.toString());
                outputDetailFile(gtp);
            }
            
            return result;
        }
        return null;        
    }

    private List<Double> getMIValues(Map<String, Double> minetResults, GoTermPair gtp) {
        List<Double> miValues = new ArrayList<Double>();
        List<String> swissIds_1 = goTermMap.get(gtp.getGoTerm1());
        List<String> swissIds_2 = goTermMap.get(gtp.getGoTerm2());

        List<String> probeIds_1 = getProbeIdsFromSwissIds(swissIds_1);
        List<String> probeIds_2 = getProbeIdsFromSwissIds(swissIds_2);


        for (String probeId_1 : probeIds_1) {
            for (String probeId_2 : probeIds_2) {
                if (!probeId_1.equals(probeId_2)) {
                    String[] ids = {probeId_1, probeId_2};
                    Arrays.sort(ids);
                    String key = ids[0] + ":" + ids[1];
                    if (minetResults.containsKey(key)) {
                        miValues.add(minetResults.get(key));
                    }
                }
            }
        }
        return miValues;

    }

    private List<String> getProbeIdsFromSwissIds(List<String> swissIds) {
        List<String> probeIds = new ArrayList<String>();
        for (String swissId : swissIds) {
            for (String probeId : swissToProbe.get(swissId)) {
                if (!probeIds.contains(probeId)) {
                    probeIds.add(probeId);
                }
            }
        }
        return probeIds;
    }

    private double[] asDoubles(List<Double> doublesList) {
        double[] doubles = new double[doublesList.size()];
        int i = 0;
        for (double d : doubles) {
            doubles[i] = d;
            i++;
        }
        return doubles;
    }

    private SummaryStatistics getSummaryStatistics(List<Double> list) {
        SummaryStatistics ss = new SummaryStatistics();
        for (double d : list) {
            ss.addValue(d);
        }

        return ss;
    }

    private void outputDetailFile(GoTermPair gtp) {
        List<String> swissIds_1 = goTermMap.get(gtp.getGoTerm1());
        List<String> swissIds_2 = goTermMap.get(gtp.getGoTerm2());

        List<String> probeIds_1 = getProbeIdsFromSwissIds(swissIds_1);
        List<String> probeIds_2 = getProbeIdsFromSwissIds(swissIds_2);

        HashMap<String, HashMap<String, Double>> detailHash = new HashMap<String, HashMap<String, Double>>();
        TreeMap<Double, String> differenceHash = new TreeMap<Double, String>();
        for (String probeId_1 : probeIds_1) {
            for (String probeId_2 : probeIds_2) {
                if (!probeId_1.equals(probeId_2)) {
                    String[] ids = {probeId_1, probeId_2};
                    Arrays.sort(ids);
                    String key = ids[0] + ":" + ids[1];
                    detailHash.put(key, new HashMap<String, Double>());
                    int count = 0;
                    if (minetResults_AL.containsKey(key)) {
                        detailHash.get(key).put("AL", minetResults_AL.get(key));
                        count++;
                    }
                    if (minetResults_AP.containsKey(key)) {
                        detailHash.get(key).put("AP", minetResults_AP.get(key));
                        count++;
                    }
                    if (minetResults_ML.containsKey(key)) {
                        detailHash.get(key).put("ML", minetResults_ML.get(key));
                        count++;
                    }
                    if (minetResults_MP.containsKey(key)) {
                        detailHash.get(key).put("MP", minetResults_MP.get(key));
                        count++;
                    }
                    if(count==4){
                        double mean_A = (minetResults_AL.get(key)+minetResults_AP.get(key))/2;
                        double mean_M = (minetResults_ML.get(key)+minetResults_MP.get(key))/2;
                        double dif = 0;
                        if(mean_A>mean_M){
                            dif = mean_A-mean_M;
                        } else {
                            dif = mean_M-mean_A;
                        }
                        double[] a = {minetResults_AL.get(key), minetResults_AP.get(key)};
                        double[] m = {minetResults_ML.get(key), minetResults_MP.get(key)};
                        MannWhitneyTest mwut = new MannWhitneyTest(a, m, H1.NOT_EQUAL);
                        differenceHash.put(dif, key+"\t"+mean_A+"\t"+mean_M+"\t"+dif+"\t"+mwut.getSP());
                        System.out.println(differenceHash.get(dif));
                    }
                }
            }
        }

    }

    private boolean include(GoTerm thisTerm) {
        if (!limitToImmuneAndCellCycle) {
            return true;
        } else {

            if (thisTerm.isDescendedFrom(new GoTerm(7049))) {

                return true;
            }
            if (thisTerm.isDescendedFrom(new GoTerm(6955))) {

                return true;
            }

//            if (thisTerm.isDescendedFrom(new GoTerm(34340))) {
//
//                return true;
//            }


            return false;
        }
    }



    private class Result {

        private double testStatistic = 0;
        private double pValue = 1;
        private SummaryStatistics ss1;
        private SummaryStatistics ss2;
        private String description;
        private double foldChange;
        private double difference;

        public Result(String description) {
            this.description = description;
        }

        public String getDescription() {
            return description;
        }

        public double getpValue() {
            return pValue;
        }

        public void setpValue(double pValue) {
            if(pValue==0.0){
                pValue = Double.MIN_VALUE;
            }
            this.pValue = pValue;
        }

        public double getTestStatistic() {
            return testStatistic;
        }

        public double getFoldChange() {
            return foldChange;
        }
        

        public void setTestStatistic(double testStatistic) {
            this.testStatistic = testStatistic;
        }

        public void setSs1(SummaryStatistics ss1) {
            this.ss1 = ss1;
        }

        public void setSs2(SummaryStatistics ss2) {
            this.ss2 = ss2;
        }

        public SummaryStatistics getSs1() {
            return ss1;
        }

        public SummaryStatistics getSs2() {
            return ss2;
        }

        public double calculateFoldChange(){
            if(ss1.getMean()>ss2.getMean()){
                foldChange = ss1.getMean()/ss2.getMean();
                difference = ss1.getMean() - ss2.getMean();
            } else {
                foldChange = ss2.getMean()/ss1.getMean();
                difference = ss2.getMean() - ss1.getMean();
            }
            return foldChange;
        }

        public double getDifference() {
            return difference;
        }

        
        

        @Override
        public String toString() {
            return description +"\t"+ss1.getN()+"\t" + + ss1.getMean() + "\t" + ss1.getStandardDeviation() + "\t" + ss2.getMean() +"\t"+ss2.getN()+"\t" + ss2.getStandardDeviation() + "\t"+foldChange+"\t"+difference+"\t" + getTestStatistic() + "\t" + getpValue();
        }
    }
}
